package middleware

import (
	"context"
	"git.mycaigou.com/gfyx/common/authorize"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/common/constants"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/common/errcode"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/di"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/utils"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/utils/auth"
	"git.mycaigou.com/gfyx/micro-gfyx-api/service/base/user"
	"github.com/gin-gonic/gin"
	"strconv"
)

func AuthorizeNew(excludes []string) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 1. 排除鉴权的路由
		if utils.InArray(excludes, ctx.Request.URL.Path) {
			ctx.Next()
			return
		}
		// 2. 获取 token , 优先从 header 上取，再从 query 查询
		token := ctx.Request.Header.Get("access_token")
		if token == "" {
			token = ctx.DefaultQuery("access_token", "")
			if token == "" {
				response(ctx, errcode.COMMON_USER_IDENTITY_ERROR, "缺少访问令牌")
				return
			}
		}
		// 3. 校验 token 是否有效, 且 token 类型为 t 是 access_token
		authUtil := auth.NewAuth()
		tokenClaim, err := authUtil.ParseToken(token)
		if err != nil || tokenClaim.Category != "t" {
			response(ctx, errcode.COMMON_TOKEN_ERROR, "令牌解析错误")
			return
		}
		// 4. 设置租户 code，租户 code 的有效性需要在 service 上校验
		ctx.Set(authorize.TENANT_CODE_KEY, tokenClaim.TenantCode)

		// 步骤4和步骤5的顺序不能乱，因为步骤5需要用到步骤4设置的租户code
		// 5. 获取登录用户信息，优先获取缓存，其次调用商机服务获取数据表的，如果都没有，则提示登录态过期
		userInfo, err := authUtil.GetLoginInfo(ctx, token)
		if err != nil {
			// 尝试通过调用商机服务获取
			userInfo, err = GetLoginUser(ctx, token)
			if err != nil {
				response(ctx, errcode.COMMON_USER_TOKEN_EXPIRED, "登录态已过期，请重新登录")
				return
			}
		}
		// 6. 设置用户登录信息上下文
		ctx.Set(constants.LoginUserToken, token)
		ctx.Set(constants.LoginUserId, userInfo.User.UserId)
		ctx.Set(constants.LoginUserInfo, userInfo)
		// 如果没有账号，则用手机号作为账号，因为调用建模的时候需要用到账号
		if userInfo.User.Account == "" {
			userInfo.User.Account = userInfo.User.Mobile
		}
		// todo 兼容历史上下文，这里逻辑已经不匹配
		authorize.SetLoginContext(
			ctx,
			strconv.Itoa(int(userInfo.User.UserId)),
			tokenClaim.TenantCode,
			userInfo.User.Account,
			userInfo.User.UserGuid,
			userInfo.User.Name,
			userInfo.User.Mobile,
			userInfo.Tenant.TenantName,
		)

		ctx.Next()
	}
}

func GetLoginUser(ctx context.Context, token string) (*auth.LoginUserInfo, error) {
	// 远程调用鉴权获取用户信息
	c := di.InjectInstance()
	loginInfo := &auth.LoginUserInfo{}
	err := c.Invoke(func(loginService user.LoginServiceIface) error {
		loginUserInfo, err := loginService.LoginUser(ctx, token)
		if err != nil {
			return err
		}
		loginInfo = loginUserInfo
		return nil
	})

	if err != nil {
		return nil, err
	}

	return loginInfo, nil

}
